feat(plans): strict-≥80%-margin tier redesign — retire every unlimited (-1)#262
Merged
Merged
Conversation
…ed" (-1)
CEO directive (2026-06-05, LOCKED): retire every `-1` ("unlimited") limit in
api/plans.yaml (the source of truth) except provisions_per_day (a
per-fingerprint rate limit, no per-GB COGS) and apply the locked number table.
Hard-caps only (no metered overage). Team stays GATED — these are the eventual
contract numbers, NOT an un-gate. Above Team = Enterprise (contact sales).
plans.yaml changes (monthly + *_yearly mirror):
- anonymous/free: queue 1024→64 MB; queue_count -1→1 (0=unlimited via
QueueCountLimit, so 1).
- hobby: queue 5120→2048 MB. pro: queue 10240→5120 MB.
- growth: vector 20480→10240; redis_commands -1→5M; mongo 20480/50/50k;
queue 20480/50; storage 153600; webhooks 100000 (all -1 retired).
- team: every -1 → finite (pg 51200/100, vector 30720/100, redis 1536/10M,
mongo 40960/50/50k, queue 40960/100, storage 307200, webhooks 100000,
members 25, vault 1000, deploys 100).
Other surfaces (rule 22):
- openapi.go: drop "$199 unlimited" + "team is unlimited" wording.
- usage_wall.go: remove the team-tier short-circuit (Team is finite now, so it
has quota walls like every other tier) — falls through to the audit query.
- Registry-iterating regression: TestPlansYAML_NoUnlimitedExceptProvisionsPerDay
reflects over every Limits int field on every tier and fails the build if any
-1 reappears (or a new int field is added with -1) except provisions_per_day.
- Synced pinning tests: QueueCountLimit, vector limits, deploys/queue caps,
webhook (team 100k; -1→10000 clamp arm now covered via a synthetic registry).
Rule-22 synchronized pricing redesign (strict-80%, retire unlimited) — see
docs/sessions/2026-06-04/PRICING-MARGIN-MODEL-AND-TEAM-REDESIGN.md.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
snapshot-drift CI gate caught the openapi.go changes (Team "$199 unlimited" → "$199 finite, not unlimited" + usage-wall "team is unlimited" → "Team has finite limits"). Regenerated via `make openapi-snapshot` (rule 22 — the dashboard + instanode-web typed clients depend on this file). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
common #46 retired every unlimited (-1) resource limit to finite caps (Team/Growth now finite). Four api handler tests still assumed the old "team/growth = unlimited" reality and reded build-and-test, which broke api master (every PR checks out common@master with the new numbers). Fixes (TESTS only — the -1-handling production code is preserved as correct defensive utilities, still reachable via provisions_per_day: -1 and future tiers): - billing_usage_coverage: TestBillingUsage_UnlimitedTier_MbToBytesNegative routed the team tier (was -1) through the HTTP usage handler to hit mbToBytes(-1). No real tier carries -1 now, so the -1 path can't be reached via a tier. Moved to a direct internal test (mb_to_bytes_internal_test.go) that feeds synthetic -1/finite inputs — keeps the defensive "-1 -> ∞" branch covered. - misc_routes_block_integration: the "team tier short-circuits to near_wall=false" subtest assumed the unlimited early-return. That early-return was removed (Team is finite -> walls apply); subtest now asserts near_wall=true with a seeded wall row. - small_handlers_final: TestUsageWallFinal_DBError_503 used failAfter=1 (team-tier pre-query #1 then audit query #2). With the early-return gone the audit query is the FIRST DB call -> failAfter=0. - team_coverage_mock: TestTeamMembers_InviteMember_LegacyMemberSuccess assumed team_members=-1 so withinMemberLimit skipped the count query. Team is now 25 -> added the teamSeatTotal (members + pending) mock expectations; 1 seat < 25 -> within limit -> 201. New rule-18 guard (strict_margin_finite_limits_test.go): iterates the LIVE plans.yaml registry and fails if any tier ships a -1 on any costed resource limit (only provisions_per_day may be -1). Re-introducing an unlimited resource cap — or a new tier with one — now reds here instead of silently shipping unbounded COGS. Team stays GATED (no checkout change). Unblocks api master. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
aeea24b to
f358aaf
Compare
mastermanas805
added a commit
that referenced
this pull request
Jun 5, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rule-22 synchronized pricing redesign (strict-80%, retire unlimited) — see
docs/sessions/2026-06-04/PRICING-MARGIN-MODEL-AND-TEAM-REDESIGN.md.CEO directive (2026-06-05, LOCKED): retire every
-1("unlimited") limit inapi/plans.yaml(source of truth) exceptprovisions_per_day(per-fingerprint rate limit, no per-GB COGS) and apply the locked table. Hard-caps only. Team stays GATED — eventual contract numbers, NOT an un-gate. Above Team = Enterprise (contact sales).plans.yaml (monthly + *_yearly mirror)
queue_count-1→1 (0 means unlimited viaQueueCountLimit, so 1).Other surfaces
openapi.go: drop "$199 unlimited" + "team is unlimited" wording.usage_wall.go: remove the team-tier short-circuit — Team is finite now and has quota walls like every other tier, so it falls through to the audit query.TestPlansYAML_NoUnlimitedExceptProvisionsPerDay: reflects over everyLimitsint field on every tier; fails the build if any-1reappears (or a new int field is added with-1) exceptprovisions_per_day.Verify
go build/vet ./...clean;go test ./internal/plans/...green; targeted handler tests green. Fullgo test ./... -shortpasses except pre-existing NATS/customer-DB/sqlmock env flakes (identical on origin/master baseline — CI provides those services).Known follow-up (NOT in this PR)
provisions_per_day: -1intentionally retained (rate limit). Separate hardening: per-service resource-COUNT caps don't exist as fields yet → a tenant could still create many resources.quota_wall_nudge.gostill skips team-tier from wall evaluation — pairs with this PR's usage_wall.go change; a follow-up should let it write team wall rows.Companion PRs: common #46, instanode-web, content.